#undef DEBUG
/*
* TclParse.java --
*
* 	A Class of the following type is filled in by Parser.parseCommand.
* 	It describes a single command parsed from an input string.
*
* Copyright (c) 1997 by Sun Microsystems, Inc.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
* Included in SQLite3 port to C# for use in testharness only;  2008 Noah B Hart
*
* RCS @(#) $Id: TclParse.java,v 1.2 1999/05/09 01:33:45 dejong Exp $
*/
using System;
namespace tcl.lang
{

  public class TclParse
  {

    // The original command string passed to Parser.parseCommand. 

    public char[] inString;

    // Index into 'string' that is the character just after the last 
    // one in the command string.

    public int endIndex;

    // Index into 'string' that is the # that begins the first of 
    // one or more comments preceding the command. 

    public int commentStart;

    // Number of bytes in comments (up through newline character 
    // that terminates the last comment).  If there were no
    // comments, this field is 0.

    public int commentSize;

    // Index into 'string' that is the first character in first 
    // word of command.

    public int commandStart;

    // Number of bytes in command, including first character of 
    // first word, up through the terminating newline, close 
    // bracket, or semicolon. 

    public int commandSize;

    // Total number of words in command.  May be 0. 

    public int numWords;

    // Stores the tokens that compose the command.

    public TclToken[] tokenList;

    // Total number of tokens in command. 

    internal int numTokens;

    //  Total number of tokens available at token.

    internal int tokensAvailable;

    /*
    *----------------------------------------------------------------------
    *
    * The fields below are intended only for the private use of the
    * parser.  They should not be used by procedures that invoke
    * Tcl_ParseCommand.
    *
    *----------------------------------------------------------------------
    */

    // Interpreter to use for error reporting, or null.

    internal Interp interp;

    // Name of file from which script came, or null.  Used for error
    // messages.

    internal string fileName;

    // Line number corresponding to first character in string. 

    internal int lineNum;

    // Points to character in string that terminated most recent token. 
    // Filled in by Parser.parseTokens.  If an error occurs, points to
    // beginning of region where the error occurred (e.g. the open brace
    // if the close brace is missing).

    public int termIndex;

    // This field is set to true by Parser.parseCommand if the command
    // appears to be incomplete.  This information is used by 
    // Parser.commandComplete.

    internal bool incomplete;

    // When a TclParse is the return value of a method, result is set to
    // a standard Tcl result, indicating the return of the method.
    public TCL.CompletionCode result;

    // Default size of the tokenList array.

    private const int INITIAL_NUM_TOKENS = 20;
    private const int MAX_CACHED_TOKENS = 50; //my tests show 50 is best

    internal TclParse( Interp interp, char[] inString, int endIndex, string fileName, int lineNum )
    {
      this.interp = interp;
      this.inString = inString;
      this.endIndex = endIndex;
      this.fileName = fileName;
      this.lineNum = lineNum;
      this.tokenList = new TclToken[INITIAL_NUM_TOKENS];
      this.tokensAvailable = INITIAL_NUM_TOKENS;
      this.numTokens = 0;
      this.numWords = 0;
      this.commentStart = -1;
      this.commentSize = 0;
      this.commandStart = -1;
      this.commandSize = 0;
      this.incomplete = false;
    }
    internal TclToken getToken( int index )
    // The index into tokenList.
    {
      if ( index >= tokensAvailable )
      {
        expandTokenArray( index );
      }

      if ( tokenList[index] == null )
      {
        tokenList[index] = grabToken();
        tokenList[index].script_array = tokenList[0].script_array;
      }
      return tokenList[index];
    }


    // Release internal resources that this TclParser object might have allocated

    internal void release()
    {
      for ( int index = 0; index < tokensAvailable; index++ )
      {
        if ( tokenList[index] != null )
        {
          releaseToken( tokenList[index] );
          tokenList[index] = null;
        }
      }
    }




    // Creating an interpreter will cause this init method to be called

    internal static void init( Interp interp )
    {
      TclToken[] TOKEN_CACHE = new TclToken[MAX_CACHED_TOKENS];
      for ( int i = 0; i < MAX_CACHED_TOKENS; i++ )
      {
        TOKEN_CACHE[i] = new TclToken();
      }

      interp.parserTokens = TOKEN_CACHE;
      interp.parserTokensUsed = 0;
    }


    private TclToken grabToken()
    {
      if ( interp == null || interp.parserTokensUsed == MAX_CACHED_TOKENS )
      {
        // either we do not have a cache because the interp is null or we have already
        // used up all the open cache slots, we just allocate a new one in this case
        return new TclToken();
      }
      else
      {
        // the cache has an avaliable slot so grab it
        return interp.parserTokens[interp.parserTokensUsed++];
      }
    }

    private void releaseToken( TclToken token )
    {
      if ( interp != null && interp.parserTokensUsed > 0 )
      {
        // if cache is not full put the object back in the cache
        interp.parserTokensUsed -= 1;
        interp.parserTokens[interp.parserTokensUsed] = token;
      }
    }


    /*
    //uncommenting these methods will disable caching
		
    static void init(Interp interp) {}
    private TclToken grabToken() {return new TclToken();}
    private void releaseToken(TclToken token) {}
    */



    internal void expandTokenArray( int needed )
    {
      // Make sure there is at least enough room for needed tokens
      while ( needed >= tokensAvailable )
      {
        tokensAvailable *= 2;
      }

      TclToken[] newList = new TclToken[tokensAvailable];
      Array.Copy( (System.Array)tokenList, 0, (System.Array)newList, 0, tokenList.Length );
      tokenList = newList;
    }

    public override string ToString()
    {

      return ( get().ToString() );
    }

    public TclObject get()
    {
      TclObject obj;
      TclToken token;
      string typeString;
      int nextIndex;
      string cmd;
      int i;


      System.Diagnostics.Debug.WriteLine( "Entered TclParse.get()" );
      System.Diagnostics.Debug.WriteLine( "numTokens is " + numTokens );

      obj = TclList.newInstance();
      try
      {
        if ( commentSize > 0 )
        {
          TclList.append( interp, obj, TclString.newInstance( new string( inString, commentStart, commentSize ) ) );
        }
        else
        {
          TclList.append( interp, obj, TclString.newInstance( "-" ) );
        }

        if ( commandStart >= ( endIndex + 1 ) )
        {
          commandStart = endIndex;
        }
        cmd = new string( inString, commandStart, commandSize );
        TclList.append( interp, obj, TclString.newInstance( cmd ) );
        TclList.append( interp, obj, TclInteger.newInstance( numWords ) );

        for ( i = 0; i < numTokens; i++ )
        {
          System.Diagnostics.Debug.WriteLine( "processing token " + i );

          token = tokenList[i];
          switch ( token.type )
          {

            case Parser.TCL_TOKEN_WORD:
              typeString = "word";
              break;

            case Parser.TCL_TOKEN_SIMPLE_WORD:
              typeString = "simple";
              break;

            case Parser.TCL_TOKEN_EXPAND_WORD:
              typeString = "expand";
              break;

            case Parser.TCL_TOKEN_TEXT:
              typeString = "text";
              break;

            case Parser.TCL_TOKEN_BS:
              typeString = "backslash";
              break;

            case Parser.TCL_TOKEN_COMMAND:
              typeString = "command";
              break;

            case Parser.TCL_TOKEN_VARIABLE:
              typeString = "variable";
              break;

            default:
              typeString = "??";
              break;

          }

          System.Diagnostics.Debug.WriteLine( "typeString is " + typeString );

          TclList.append( interp, obj, TclString.newInstance( typeString ) );
          TclList.append( interp, obj, TclString.newInstance( token.TokenString ) );
          TclList.append( interp, obj, TclInteger.newInstance( token.numComponents ) );
        }
        nextIndex = commandStart + commandSize;
        TclList.append( interp, obj, TclString.newInstance( new string( inString, nextIndex, ( endIndex - nextIndex ) ) ) );
      }
      catch ( TclException e )
      {
        // Do Nothing.
      }

      return obj;
    }
  } // end TclParse
}
